import bisect
import contextlib
import logging
import uuid

import redis
from redis import Redis
import time
from datetime import datetime

mconn = Redis(host="192.168.35.108", port=6379, db=0, decode_responses=True)
sconn = Redis(host="192.168.35.108", port=6380, db=0, decode_responses=True)

PRECISION = [1, 5, 60, 300, 3600, 18000, 86400]


def update_counter(conn, name, count=1, now=None):
    now = now or time.time()
    pipe = conn.pipeline()
    for prec in PRECISION:
        pnow = int(now / prec) * prec
        hash = '%s:%s' % (prec, name)
        pipe.zadd('known:', {hash: 0})
        pipe.hincrby('count:' + hash, pnow, count)
    pipe.execute()


def get_counter(conn, name, precision):
    hash = '%s:%s' % (precision, name)
    data = conn.hgetall('count:' + hash)
    to_return = []
    for key, value in data.items():
        to_return.append((int(key), int(value)))
    to_return.sort()
    return to_return


QUIT = False

SAMPLE_COUNT = 3


def clean_counter(conn):
    pipe = conn.pipeline(True)
    passes = 0
    while not QUIT:
        start = time.time()
        index = 0
        while index < conn.zcard('known:'):
            hash = conn.zrange('known:', index, index)
            index += 1
            if not hash:
                break;
            hash = hash[0]
            prec = int(hash.partition(':')[0])
            bprec = int(prec // 60) or 1
            if passes % bprec:
                continue

            hkey = 'count:' + hash
            cutoff = time.time() - SAMPLE_COUNT * prec
            samples = list(map(int, conn.hkeys(hkey)))
            samples.sort()
            remove = bisect.bisect_right(samples, cutoff)
            if remove:
                conn.hdel(hkey, *samples[:remove])
                if remove == len(samples):
                    try:
                        pipe.watch(hkey)
                        if not pipe.hlen(hkey):
                            pipe.multi()
                            pipe.zrem('known:', hash)
                            pipe.execute()
                            index -= 1
                        else:
                            pipe.unwatch()
                    except redis.exceptions.WatchError:
                        pass
            passes += 1
            duration = min(int(time.time() - start) + 1, 60)
            time.sleep(max(60 - duration, 1))


def update_status(conn, context, type, value, timeout=5):
    destination = 'stats:%s:%s' % (context, type)
    start_key = destination + ':start'
    pipe = conn.pipeline(True)
    end = time.time() + timeout
    while time.time() < end:
        try:
            pipe.watch(start_key)
            now = datetime.utcnow().timetuple()
            hour_start = datetime(*now[:4]).isoformat()
            existing = pipe.get(start_key)
            pipe.multi()
            if existing and existing < hour_start:
                pipe.rename(destination, destination + ':last')
                pipe.rename(start_key, destination + ':pstart')
                pipe.set(start_key, hour_start)
            tkey1 = str(uuid.uuid4())
            tkey2 = str(uuid.uuid4())
            pipe.zadd(tkey1, 'min', value)
            pipe.zadd(tkey2, 'max', value)
            pipe.zunionstore(destination, [destination, tkey1], aggregate='min')
            pipe.zunionstore(destination, [destination, tkey2], aggregate='max')
            pipe.delete(tkey1, tkey2)
            pipe.zincrby(destination, 'count')
            pipe.zincrby(destination, 'sum', value)
            pipe.zincrby(destination, 'sumsq', value * value)
            return pipe.execute()[-3:]
        except redis.exceptions.WatchError:
            continue


@contextlib.contextmanager
def access_time(conn, context):
    start = time.time()
    yield
    delta = time.time() - start
    pipe = conn.pipeline(True)
    pipe.zadd('slowest:AccessTime', {context:delta})
    pipe.zremrangebyrank('slowest:AccessTime', 0, -101)
    pipe.execute()


def print_hi(mconn,callback):
    with access_time(mconn,"test"):
        return callback()

def print_h():
    print("hello")



# 按间距中的绿色按钮以运行脚本。
if __name__ == '__main__':
    update_counter(mconn, "点击")
    get_counter(mconn, "点击", '300')
    #clean_counter(mconn)
    print_hi(mconn,print_h)

# 访问 https://www.jetbrains.com/help/pycharm/ 获取 PyCharm 帮助
